package com.ffe.flower.engine.component;

import java.util.Collections;
import java.util.List;

import javax.transaction.Transactional;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import com.ffe.flower.engine.dto.FlowCriteriaQuery;
import com.ffe.flower.engine.service.IFlowStateDetailService;
import com.ffe.flower.engine.util.ApplicationContextHelper;
import com.ffe.flower.engine.wrapper.FlowStateWrapper;
import com.ffe.flower.engine.wrapper.FlowWrapper;

@Scope("prototype")
@Component
public class FlowInstance {

  private static IFlowStateDetailService flowStateDetailService = ApplicationContextHelper
      .getBean(IFlowStateDetailService.class);

  private String processId;

  private Integer flowId;

  private Object field;

  private ApplicantForm applicantForm;

  private AuditorForm auditorForm;

  public FlowInstance(String processId) {
    super();
    this.processId = processId;
  }

  public FlowInstance(Integer flowId) {
    super();
    this.flowId = flowId;
  }

  public FlowInstance field(Object field) {
    this.field = field;
    return this;
  }

  public FlowInstance asApplicant(String applicantId, String targetId) {
    this.applicantForm = new ApplicantForm(applicantId, targetId);
    return this;
  }

  public FlowInstance asApplicant(String applicantId, String targetId, String promoterId, boolean promoterAgent) {
    this.applicantForm = new ApplicantForm(applicantId, targetId, promoterId, promoterAgent);
    return this;
  }

  public FlowInstance asAuditor(String auditorId) {
    this.auditorForm = new AuditorForm(auditorId);
    return this;
  }

  public FlowInstance asAuditor(String auditorId, boolean auditorAgent, String remark) {
    this.auditorForm = new AuditorForm(auditorId, auditorAgent, remark);
    return this;
  }

  @Transactional
  public FlowWrapper start() {
    return sendEvent(null);
  }

  @Transactional
  public FlowWrapper sendEvent(Enum<?> event) {
    FlowEvent flowEvent = null;
    if (this.processId == null) {
      flowEvent = new FlowEvent(event);
    } else {
      flowEvent = new FlowEvent(null);
    }
    flowEvent.setField(field);
    flowEvent.setApplicantForm(applicantForm);
    flowEvent.setAuditorForm(auditorForm);
    flowEvent.setFlowId(flowId);
    flowEvent.setProcessId(processId);
    ApplicationContextHelper.getApplicationContext().publishEvent(flowEvent);
    if (flowId == null) {
      flowId = flowEvent.getFlowWrapperAfterEvent().getFlowId();
    }
    return flowEvent.getFlowWrapperAfterEvent();
  }

  public List<FlowStateWrapper> wholeStates() {
    return this.flowId == null ? Collections.emptyList() : flowStateDetailService.getWholeFlowState(this.flowId);
  }

  public FlowWrapper get() {
    if (flowId != null) {
      FlowCriteriaQuery query = new FlowCriteriaQuery();
      query.setFlowId(flowId);
      List<FlowWrapper> flowInfos = FlowEngine.query(query);
      if (!flowInfos.isEmpty()) {
        return flowInfos.get(0);
      }
    }
    return null;
  }

  public class FlowEvent {

    private Enum<?> event;

    private Object field;

    private ApplicantForm applicantForm;

    private AuditorForm auditorForm;

    private String processId;

    private Integer flowId;

    private FlowWrapper flowWrapperAfterEvent;

    public FlowEvent(Enum<?> event) {
      super();
      this.event = event;
    }

    public String getProcessId() {
      return processId;
    }

    public void setProcessId(String processId) {
      this.processId = processId;
    }

    public Integer getFlowId() {
      return flowId;
    }

    public void setFlowId(Integer flowId) {
      this.flowId = flowId;
    }

    public Enum<?> getEvent() {
      return event;
    }

    public void setEvent(Enum<?> event) {
      this.event = event;
    }

    public ApplicantForm getApplicantForm() {
      return applicantForm;
    }

    public void setApplicantForm(ApplicantForm applicantForm) {
      this.applicantForm = applicantForm;
    }

    public AuditorForm getAuditorForm() {
      return auditorForm;
    }

    public void setAuditorForm(AuditorForm auditorForm) {
      this.auditorForm = auditorForm;
    }

    public FlowWrapper getFlowWrapperAfterEvent() {
      return flowWrapperAfterEvent;
    }

    public void setFlowWrapperAfterEvent(FlowWrapper flowWrapperAfterEvent) {
      this.flowWrapperAfterEvent = flowWrapperAfterEvent;
    }

    public Object getField() {
      return field;
    }

    public void setField(Object field) {
      this.field = field;
    }

  }

  public class ApplicantForm {
    private String applicantId;
    private String targetId;
    private String promoterId;
    private boolean promoterAgent;

    public ApplicantForm(String applicantId, String targetId, String promoterId, boolean promoterAgent) {
      super();
      this.applicantId = applicantId;
      this.targetId = targetId;
      this.promoterId = promoterId;
      this.promoterAgent = promoterAgent;
    }

    public ApplicantForm(String applicantId, String targetId) {
      super();
      this.applicantId = applicantId;
      this.targetId = targetId;
      this.promoterId = applicantId;
    }

    public String getApplicantId() {
      return applicantId;
    }

    public void setApplicantId(String applicantId) {
      this.applicantId = applicantId;
    }

    public String getTargetId() {
      return targetId;
    }

    public void setTargetId(String targetId) {
      this.targetId = targetId;
    }

    public String getPromoterId() {
      return promoterId;
    }

    public void setPromoterId(String promoterId) {
      this.promoterId = promoterId;
    }

    public boolean isPromoterAgent() {
      return promoterAgent;
    }

    public void setPromoterAgent(boolean promoterAgent) {
      this.promoterAgent = promoterAgent;
    }
  }

  public class AuditorForm {
    private String auditorId;
    private boolean auditorAgent;
    private String remark;

    public AuditorForm(String auditorId) {
      super();
      this.auditorId = auditorId;
    }

    public AuditorForm(String auditorId, boolean auditorAgent, String remark) {
      super();
      this.auditorId = auditorId;
      this.auditorAgent = auditorAgent;
      this.remark = remark;
    }

    public String getAuditorId() {
      return auditorId;
    }

    public void setAuditorId(String auditorId) {
      this.auditorId = auditorId;
    }

    public boolean isAuditorAgent() {
      return auditorAgent;
    }

    public void setAuditorAgent(boolean auditorAgent) {
      this.auditorAgent = auditorAgent;
    }

    public String getRemark() {
      return remark;
    }

    public void setRemark(String remark) {
      this.remark = remark;
    }
  }

}
